﻿/*
 * Autore: Artur Tolstenco
 */

using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Win32;
using RecipesManager.Data;

using RecipesManager.Data.Operations;

namespace RecipesManager.MainControls.RecipeSubControls
{
    /// <summary>
    /// Interaction logic for RecipeMainPane.xaml
    /// </summary>
    public partial class RecipeMainPane
    {
        public bool Editing;
        IRecipe _currentRecipe;

        internal readonly ObservableCollection<IngredientFromRecipe> ingredients;
        internal readonly ObservableCollection<StepFromRecipe> steps; 

        public RecipeMainPane()
        {
            InitializeComponent();

            var max = Data.Properties.Settings.Default.MaxHours;
            var min = Data.Properties.Settings.Default.MinHours;
            var step = Data.Properties.Settings.Default.StepHours;

            for (var i = min; i <= max; i += step)
                cb_tempoOre.Items.Add(i);

            max = Data.Properties.Settings.Default.MaxMinutes;
            min = Data.Properties.Settings.Default.MinMinutes;
            step = Data.Properties.Settings.Default.StepMinutes;

            for (var i = min; i <= max; i += step)
                cb_tempoMin.Items.Add(i);

            max = Data.Properties.Settings.Default.MaxServings;
            min = Data.Properties.Settings.Default.MinServings;
            step = Data.Properties.Settings.Default.StepServings;

            for (var i = min; i <= max; i += step)
                cb_numeroPorzioni.Items.Add(i);

            cb_famigliUnitaMisura.Items.Add(Properties.Resources.MetricMeasureFamily);
            cb_famigliUnitaMisura.Items.Add(Properties.Resources.UKMeasureFamily);
            cb_famigliUnitaMisura.Items.Add(Properties.Resources.USAMeasureFamily);

            ingredients = new ObservableCollection<IngredientFromRecipe>();
            lv_ingredients.ItemsSource = ingredients;

            steps = new ObservableCollection<StepFromRecipe>();
            lv_steps.ItemsSource = steps;
        }

        public string CurrentRecipe
        {
            get { return _currentRecipe == null ? "" : tb_nomeRicetta.Text; }
            set {
                Mouse.OverrideCursor = Cursors.Wait;
                tb_nomeRicetta.Text = value;
                MyInitialize();
                tb_nomeRicetta.SelectionStart = tb_nomeRicetta.Text.Length;
                tb_nomeRicetta.Focus();
                Editing = true;
                Mouse.OverrideCursor = null;
            }
        }

        public MeasureFamily CurrentMeasureFamily
        {
            get { return GetMeasureFamilyType(cb_famigliUnitaMisura.SelectedItem as string); }
        }

        void MyInitialize()
        {
            bt_del_ingredient.IsEnabled = bt_cancella_passo.IsEnabled = false;
            bt_edit_ingredient.IsEnabled = bt_modifica_passo.IsEnabled = false;
            tb_fonteOriginale.Text = "";
            cb_tempoOre.SelectedItem = cb_tempoMin.SelectedItem = null;
            cb_numeroPorzioni.SelectedItem = null;
            cb_famigliUnitaMisura.SelectedItem = null;

            ingredients.Clear();
            steps.Clear();
            lv_trials.Items.Clear();
            
            //_currentRecipe.Steps.a
            if (tb_nomeRicetta.Text.Equals(""))
            {
                _currentRecipe = null;
                bt_vis_originale.IsEnabled = false;
                bt_salva_originale.IsEnabled = false;
                //bt_stamp.IsEnabled = false;
            }
            else
            {
                _currentRecipe = Manager.Recipes[tb_nomeRicetta.Text];

                bt_vis_originale.IsEnabled = _currentRecipe.HasOriginal;
                bt_salva_originale.IsEnabled = !_currentRecipe.HasOriginal;

                tb_fonteOriginale.Text = _currentRecipe.Source;
                cb_tempoOre.SelectedItem = _currentRecipe.PrepHours;
                cb_tempoMin.SelectedItem = _currentRecipe.PrepMinutes;
                cb_numeroPorzioni.SelectedItem = _currentRecipe.Servings;

                cb_famigliUnitaMisura.SelectedItem = GetMeasureFamilyString(_currentRecipe.MeasureFamily);

                foreach (var i in _currentRecipe.Ingredients)
                    ingredients.Add(new IngredientFromRecipe(i.Ingredient.Name, i.Notes, i.Quantity, i.Measure.Name));

                
                foreach (var s in _currentRecipe.Steps)
                    steps.Add(new StepFromRecipe(s));

                if (steps.Count >= 1)
                {
                    if (steps[0].UpdatedId == 0)
                        StepFromRecipe.BaseID = -1;
                    else
                        StepFromRecipe.BaseID = steps[0].UpdatedId - 1;
                }

                foreach (var recipeTrial in _currentRecipe.Trials)
                    lv_trials.Items.Add(recipeTrial.Notes);

                bt_vis_originale.IsEnabled = _currentRecipe.HasOriginal;
                bt_salva_originale.IsEnabled = !_currentRecipe.HasOriginal;

                //bt_stamp.IsEnabled = true;
            }

            bt_salva.IsEnabled = IsModified();
            

        }

        public bool IsModified()
        {
            if (_currentRecipe == null)
                return true;

            if (!tb_nomeRicetta.Text.ToLower().Equals(_currentRecipe.Name.ToLower()))
                return true;

            if (!tb_fonteOriginale.Text.ToLower().Equals(_currentRecipe.Source.ToLower()))
                return true;

            if (RecipeAndInterfaceNotEquals(_currentRecipe.PrepHours, cb_tempoOre.SelectedItem as int?) ||
                RecipeAndInterfaceNotEquals(_currentRecipe.PrepMinutes, cb_tempoMin.SelectedItem as int?) ||
                RecipeAndInterfaceNotEquals(_currentRecipe.Servings, cb_numeroPorzioni.SelectedItem as int?))
                return true;

            if (_currentRecipe.MeasureFamily != GetMeasureFamilyType(cb_famigliUnitaMisura.SelectedItem as string))
                return true;


            //check the ingredients;
            if (ingredients.Count != _currentRecipe.Ingredients.Count)
                return true;

            foreach (var oldIngredient in _currentRecipe.Ingredients)
            {
                var i = ingredients.IndexOf(new IngredientFromRecipe(oldIngredient.Name));
                if (i == -1)
                    return true;

                var oldModIngredient = ingredients[i];
                if (!oldIngredient.Notes.ToLower().Equals(oldModIngredient.Notes.ToLower()) ||
                    Math.Abs(oldIngredient.Quantity - oldModIngredient.Quantity) > Double.Epsilon ||
                    !oldIngredient.Measure.Name.ToLower().Equals(oldModIngredient.Measure.ToLower()))
                    return true;
            }

            //check steps
            if (steps.Count != _currentRecipe.Steps.Count)
                return true;

            foreach (var oldStep in _currentRecipe.Steps)
            {
                var s = steps.FirstOrDefault(ss => !ss.IsNewStep && ss.Id == oldStep.Id);
                if (s == null)
                    return true;
                if (s.UpdatedId != oldStep.Id || s.Type != oldStep.Type)
                    return true;

                if (s.StepHours != oldStep.StepHours || s.StepMinutes != oldStep.StepMinutes)
                    return true;
                if (s.Type == StepType.SubRecipe)
                {
                    if (!s.SubRecipeName.Equals(oldStep.SubRecipe.Name) || s.Servings != oldStep.Servings)
                        return true;
                }
                else
                {
                    if (!s.Description.Equals(oldStep.Description) || !s.Resources.Equals(oldStep.Resources))
                        return true;
                }
            }

            //check prove
            if (lv_trials.Items.Count != _currentRecipe.Trials.Count)
                return true;

            return false;
        }

        
        public bool SaveCurrentRecipe()
        {
            try
            {
                if (tb_nomeRicetta.Text.Trim().Equals(""))
                {
                    MessageBox.Show("Si assicuri che il nome della ricetta non sia vuoto.", "Avviso",
                                    MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                    tb_nomeRicetta.SelectAll();
                    tb_nomeRicetta.Focus();
                    return false;
                }

                if (_currentRecipe == null)
                {
                    //try catch
                    _currentRecipe = Manager.Recipes.Add(tb_nomeRicetta.Text,
                                                         tb_fonteOriginale.Text,
                                                         GetMeasureFamilyType(
                                                             cb_famigliUnitaMisura.SelectedItem as string),
                                                         cb_tempoOre.SelectedItem as int?,
                                                         cb_tempoMin.SelectedItem as int?,
                                                         cb_numeroPorzioni.SelectedItem as int?);
                }
                else
                {
                    //try catch
                    _currentRecipe.Update(tb_nomeRicetta.Text,
                                          tb_fonteOriginale.Text,
                                          GetMeasureFamilyType(cb_famigliUnitaMisura.SelectedItem as string),
                                          cb_tempoOre.SelectedItem as int?, cb_tempoMin.SelectedItem as int?,
                                          cb_numeroPorzioni.SelectedItem as int?);
                }

                tb_nomeRicetta.Text = _currentRecipe.Name;

                //rimuovo e aggiorno gli ingredienti esistenti
                foreach (var i in _currentRecipe.Ingredients)
                {
                    var i1 = i;
                    var updatedI = ingredients.FirstOrDefault(ingr => ingr.Name.Equals(i1.Name));

                    if (updatedI == null)
                        _currentRecipe.Ingredients.Remove(i.Id);
                    else //tryCatch...
                        i.Update(updatedI.Measure, updatedI.Quantity, updatedI.Notes);
                }

                //aggiungo gli ingredienti nuovi
                foreach (
                    var i in ingredients.Where(i => !_currentRecipe.Ingredients.Any(cri => cri.Name.Equals(i.Name))))
                    _currentRecipe.Ingredients.Add(i.Name, i.Measure, i.Quantity, i.Notes);


                //mi occupo di salvare i passi usare il try cach
                _currentRecipe.Steps.Clear();
                foreach (var s in steps)
                    if (s.Type == StepType.SubRecipe)
                        _currentRecipe.Steps.AddSubRecipe(s.SubRecipeName, s.Servings);
                    else
                        _currentRecipe.Steps.AddGeneric(s.Description, s.Resources, s.StepHours, s.StepMinutes);


                steps.Clear();
                foreach (var s in _currentRecipe.Steps)
                    steps.Add(new StepFromRecipe(s));

                if (steps.Count >= 1)
                {
                    if (steps[0].UpdatedId == 0)
                        StepFromRecipe.BaseID = -1;
                    else
                        StepFromRecipe.BaseID = steps[0].UpdatedId - 1;
                }

                //mi occupo di salvare le note

                for (var i = _currentRecipe.Trials.Count; i < lv_trials.Items.Count; i++)
                    _currentRecipe.Trials.Add(lv_trials.Items[i] as string);

            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, (string)Application.Current.FindResource("notification_dialog_type"),
                                    MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                return false;
            }

            bt_salva.IsEnabled = false;
            bt_salva_originale.IsEnabled = !_currentRecipe.HasOriginal;
            bt_vis_originale.IsEnabled = _currentRecipe.HasOriginal;

            //bt_stamp.IsEnabled = true;
            
            return true;
        }

        private void Lv_Ingredients_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var l = sender as ListView;

            if (l == null)
                return;


            if (l.Equals(lv_ingredients))
            {
                bt_del_ingredient.IsEnabled = bt_edit_ingredient.IsEnabled = l.SelectedItem != null;
            }
            else if (l.Equals(lv_steps))
            {
                bt_cancella_passo.IsEnabled = bt_modifica_passo.IsEnabled = l.SelectedItem != null;
            }
        }

        private void Tb_NomeRicetta_TextChanged(object sender, TextChangedEventArgs e)
        {
            bt_salva.IsEnabled = IsModified();
        }

        private void Cb_TempoOre_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            bt_salva.IsEnabled = IsModified();
        }


        #region buttons
        private void Bt_Del_Ingredient_Click(object sender, RoutedEventArgs e)
        {
            var selectedIngredient = lv_ingredients.SelectedItem as IngredientFromRecipe;
            if (selectedIngredient == null)
                return;
            ingredients.Remove(selectedIngredient);

            bt_salva.IsEnabled = IsModified();
        }

        private void Bt_Salva_Originale_Click(object sender, RoutedEventArgs e)
        {
            if (IsModified())
            {
                MessageBox.Show("Salva originale può essere fatto solo dopo aver fatto un salva generale.",
                            (string)Application.Current.FindResource("notification_dialog_type"),
                            MessageBoxButton.OK,
                            MessageBoxImage.Error);
                return;
            }
            
            _currentRecipe.MakeOriginal();


            bt_salva_originale.IsEnabled = !_currentRecipe.HasOriginal;
            bt_vis_originale.IsEnabled = _currentRecipe.HasOriginal;
        }


        private void Bt_Cancella_Passo_Click(object sender, RoutedEventArgs e)
        {
            var selectedStep = lv_steps.SelectedItem as StepFromRecipe;
            if (selectedStep == null)
                return;

            var indexSelectedStep = steps.IndexOf(selectedStep);
            for (var i = indexSelectedStep + 1; i < steps.Count; i++)
            {
                var curS = steps[i];
                steps.RemoveAt(i);
                curS.UpdatedId--;
                steps.Insert(i, curS);
            }
            steps.RemoveAt(indexSelectedStep);

            bt_salva.IsEnabled = IsModified();
        }
        #endregion


        #region utility

        static bool RecipeAndInterfaceNotEquals(int? first, int? second)
        {
            if (first.HasValue)
            {
                if (second == null || second != first.Value)
                    return true;
            }
            else
            {
                if (second != null)
                    return true;
            }
            return false;
        }

        static internal string GetMeasureFamilyString(MeasureFamily mF)
        {
            switch (mF)
            {
                case MeasureFamily.UK:
                    return Properties.Resources.UKMeasureFamily;
                case MeasureFamily.USA:
                    return Properties.Resources.USAMeasureFamily;
                case MeasureFamily.Metric:
                    return Properties.Resources.MetricMeasureFamily;
                case MeasureFamily.None:
                    return null;
            }
            return null;
        }

        static MeasureFamily GetMeasureFamilyType(string s)
        {
            if (s == null)
                return MeasureFamily.None;

            if (s.Equals(Properties.Resources.MetricMeasureFamily))
                return MeasureFamily.Metric;
            return s.Equals(Properties.Resources.UKMeasureFamily) ? MeasureFamily.UK : MeasureFamily.USA;
        }

        #endregion

        private void Bt_Vis_Originale_Click(object sender, RoutedEventArgs e)
        {
            var viewOriginalRecipeWindow = new ViewOriginalRecipeWindow {CurrentRecipe = _currentRecipe};
            viewOriginalRecipeWindow.ShowDialog();
        }

        private void Bt_Stamp_Click(object sender, RoutedEventArgs e)
        {
            if (IsModified())
            {
                var r = MessageBox.Show("Per stampare lo stato attuale della ricetta si prega di salvare prima. Si vuole salvare e poi stampare?", (string) Application.Current.FindResource("notification_dialog_type"), MessageBoxButton.YesNo, MessageBoxImage.Warning);
                if (r == MessageBoxResult.Yes)
                {
                    if (!SaveCurrentRecipe())
                        return;
                }
                else
                    return;

            }
            var dialog = new SaveFileDialog { Title = "Stampa su file la ricetta corrente", FileName = _currentRecipe.Name, DefaultExt = ".html", Filter = "HTML|*.html" };

            var result = dialog.ShowDialog();

            if (result != true) return;

            try
            {
                File.WriteAllText(dialog.FileName, _currentRecipe.ToHtml());
            }
            catch
            {
                MessageBox.Show("E' accadutto un errore durante la stampa della ricetta. Riprovi.",
                                (string) Application.Current.FindResource("notification_dialog_type"),
                                MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
            MessageBox.Show("La ricetta è stata stampata sul file correttamente.", (string)Application.Current.FindResource("notification_dialog_type"), MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }
}
